/*
 * Copyright (c) 2017 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */


#ifndef ClosureWriter_h
#define ClosureWriter_h


#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <uuid/uuid.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <uuid/uuid.h>

#include "Closure.h"



namespace dyld3 {

namespace closure {


class VIS_HIDDEN ContainerTypedBytesWriter
{
public:
    void        deallocate();

protected:
    void        setContainerType(TypedBytes::Type containerType);
    void*       append(TypedBytes::Type t, const void* payload, uint32_t payloadSize);

    const void* currentTypedBytes();
    const void* finalizeContainer();

    void*       _vmAllocationStart      = nullptr;
    size_t      _vmAllocationSize       = 0;
    TypedBytes* _containerTypedBytes    = nullptr;
    void*       _end                    = nullptr;
};


class VIS_HIDDEN ImageWriter : public ContainerTypedBytesWriter
{
public:

    void        setImageNum(ImageNum num);
    void        addPath(const char* path); // first is canonical, others are aliases
    void        setInvalid();
    void        setInDyldCache(bool);
    void        setIs64(bool);
    void        setHasObjC(bool);
    void        setHasPlusLoads(bool);
    void        setIsBundle(bool);
    void        setIsDylib(bool);
    void        setIsExecutable(bool);
    void        setIsRestricted(bool);
    void        setHasWeakDefs(bool);
    void        setUses16KPages(bool);
    void        setOverridableDylib(bool);
    void        setNeverUnload(bool);
    void        setUUID(const uuid_t uuid);
    void        setCDHash(const uint8_t cdHash[20]);
    void        setDependents(const Array<Image::LinkedImage>& deps);
    void        setDofOffsets(const Array<uint32_t>& dofSectionOffsets);
    void        setInitOffsets(const uint32_t initOffsets[], uint32_t count);
    void        setDiskSegments(const Image::DiskSegment segs[], uint32_t count);
    void        setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count);
    void        setCodeSignatureLocation(uint32_t fileOffset, uint32_t size);
    void        setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size);
    void        setMappingInfo(uint64_t sliceOffset, uint64_t vmSize);
    void        setFileInfo(uint64_t inode, uint64_t modTime);
    void        setRebaseInfo(const Array<Image::RebasePattern>&);
    void        setTextRebaseInfo(const Array<Image::TextFixupPattern>&);
    void        setBindInfo(const Array<Image::BindPattern>&);
    void        setAsOverrideOf(ImageNum);
    void        addExportPatchInfo(uint32_t implOff, const char* name, uint32_t locCount, const Image::PatchableExport::PatchLocation* locs);
    void        setInitsOrder(const ImageNum images[], uint32_t count);
    void        setChainedFixups(const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets);

    const Image* currentImage();

    const Image* finalize();

private:
    Image::Flags& getFlags();

    int   _flagsOffset = -1;
};


class VIS_HIDDEN ImageArrayWriter : public ContainerTypedBytesWriter
{
public:
                        ImageArrayWriter(ImageNum startImageNum, unsigned count);

    void                appendImage(const Image*);
    const ImageArray*   finalize();
private:
    unsigned            _index;
};

class VIS_HIDDEN ClosureWriter : public ContainerTypedBytesWriter
{
public:
    void                    setTopImageNum(ImageNum imageNum);
    void                    addCachePatches(const Array<Closure::PatchEntry>&);
};

class VIS_HIDDEN LaunchClosureWriter : public ClosureWriter
{
public:
                            LaunchClosureWriter(const ImageArray* images);

    const LaunchClosure*    finalize();
    void                    setLibSystemImageNum(ImageNum imageNum);
    void                    setInitImageCount(uint32_t count);
    void                    setLibDyldEntry(Image::ResolvedSymbolTarget dyldEntry);
    void                    setMainEntry(Image::ResolvedSymbolTarget main);
    void                    setStartEntry(Image::ResolvedSymbolTarget start);
    void                    setUsedFallbackPaths(bool);
    void                    setUsedAtPaths(bool);
    void                    setMustBeMissingFiles(const Array<const char*>& paths);
    void                    addInterposingTuples(const Array<InterposingTuple>& tuples);
    void                    setDyldCacheUUID(const uuid_t);
    void                    setBootUUID(const char* uuid);
    void                    applyInterposing();
    void                    addEnvVar(const char* envVar);

private:
    LaunchClosure::Flags&   getFlags();

    int                      _flagsOffset = -1;
};


class VIS_HIDDEN DlopenClosureWriter : public ClosureWriter
{
public:
                             DlopenClosureWriter(const ImageArray* images);

    const DlopenClosure*     finalize();

};


} //  namespace closure
} //  namespace dyld3


#endif // ClosureWriter_h

